﻿//-----------------------------------------------------------------------
// <copyright file="PackageRepository.cs" company="FastNET Services">
//     Copyright (c) 2008-2009. All Rights Reserved.
// </copyright>
// <license>
//     GPLv2
// </license>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.EntityClient;
using System.Linq;
using System.Transactions;
using FastNET.CleanAct.Client.ManagerEngineData.Entities;
using FastNET.CleanAct.Common.Repository;
using FastNET.CleanAct.Common.Repository.Interfaces.DataInterfaces.ManagementEngine;
using FastNET.CleanAct.Common.Repository.Interfaces.Repositories.ManagementEngine;


#endregion

namespace FastNET.CleanAct.Client.ManagerEngineData.Repositories
{
    /// <summary>
    /// General repository for managing Packages
    /// </summary>
    internal class PackageRepository
        : IPackageRepository
    {
        #region CTor/DTor

        /// <summary>
        /// Initializes a new instance of the PackageRepository class
        /// </summary>
        /// <param name="connection">Reference to the common EntityConnection for the repository</param>
        internal PackageRepository(EntityConnection connection)
        {
            Connection = connection;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a reference to the common EntityConnection for the repository
        /// </summary>
        protected EntityConnection Connection { get; private set; }

        #endregion

        #region IRepository<IPackageData> Members

        /// <summary>
        /// Retrieves all instances of IPackageData from the repository
        /// </summary>
        /// <returns>IEnumerable of the requested IData</returns>
        public IList<IPackageData> Retrieve()
        {
            List<IPackageData> returnValues = new List<IPackageData>();

            using (ManagerDataEntities dataSource = new ManagerDataEntities(Connection))
            {
                IEnumerable<Package> query = from item in dataSource.Packages select item;

                foreach (Package item in query)
                {
                    returnValues.Add(item);
                    dataSource.Detach(item);
                }
            }

            return returnValues;
        }

        /// <summary>
        /// Retrieves the requested IPackageData by an Identity
        /// </summary>
        /// <param name="ID">Identity to retrieve the data by</param>
        /// <returns>IData instance or null if one was not found</returns>
        /// <exception cref="InvalidCastException">Thrown if the identity is of the wrong type for this datasource</exception>
        public IPackageData Retrieve(Identity ID)
        {
            if (!ID.CanFitInGuid())
            {
                throw new InvalidCastException(string.Format("Identity for this type of repository requires it to be of Guid Type. Currently is {0}.", ID.UnderlyingType()));
            }

            // This has to be done here because LINQ does not support the inline case
            Guid searchID = ID;

            using (ManagerDataEntities dataSource = new ManagerDataEntities(Connection))
            {
                Package foundItem = (from
                                                item
                                                in
                                         dataSource.Packages
                                     where
                                         item.PackageID == searchID
                                     select
                                         item).FirstOrDefault();

                if (foundItem != null)
                {
                    dataSource.Detach(foundItem);
                }

                return foundItem;
            }
        }

        /// <summary>
        /// Checks for an existing Package Name in the database
        /// </summary>
        /// <param name="name">Name to check for</param>
        /// <param name="ignoreID">Optional ID to ignore during purposes of renaming</param>
        /// <returns>Boolean indicating whether or not the name exists</returns>
        public bool CheckExistingName(string name, Identity? ignoreID)
        {
            using (ManagerDataEntities dataSource = new ManagerDataEntities(Connection))
            {
                if (ignoreID.HasValue)
                {
                    if (!ignoreID.Value.CanFitInGuid())
                    {
                        throw new InvalidCastException("IgnoreID must be of type Guid for this repository");
                    }

                    Guid searchID = ignoreID.Value;
                    return (from
                                    item
                                in
                                dataSource.Packages.Where("it.Name == '" + name + "'") //Stupid MS Issues
                            where
                                item.PackageID != searchID
                            select
                                item).Count() > 0;
                }

                return (from
                                item
                            in
                            dataSource.Packages.Where("it.Name == '" + name + "'") //Stupid MS Issues
                        select
                            item).Count() > 0;
            }
        }

        /// <summary>
        /// Retrieves a package by the name
        /// </summary>
        /// <param name="name">Name of the package to retrieve</param>
        /// <returns>IPackageData of the item found or null if a package was not found</returns>
        public IPackageData Retrieve(string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }

            using (ManagerDataEntities dataSource = new ManagerDataEntities(Connection))
            {
                Package package = (from
                            item
                        in
                                       dataSource.Packages.Where("it.Name = '" + name + "'") //Stupid MS Issues
                                   select
                                       item).FirstOrDefault();

                if (package != null)
                {
                    dataSource.Detach(package);
                }

                return package;
            }
        }

        /// <summary>
        /// Deletes the requested IPackageData
        /// </summary>
        /// <param name="Data">IPackageData to delete from the repository</param>
        /// <exception cref="ArgumentNullException">Thrown if data is null</exception>
        /// <exception cref="InvalidCastException">Thrown if the data is of the wrong type for this datasource</exception>
        /// <exception cref="InvalidOperationException">Thrown if the IPackageData is new or already deleted</exception>
        public void Delete(IPackageData Data)
        {
            if (Data == null)
            {
                throw new ArgumentNullException("Data", "Must have a valid Data to delete");
            }

            if (!(Data is Package))
            {
                throw new InvalidCastException("Data is not of type Package. Cannot delete this object through this repository");
            }

            if (Data.IsNew)
            {
                throw new InvalidOperationException("Data cannot be new before deleting. There is nothing to delete.");
            }

            if (Data.IsDeleted)
            {
                throw new InvalidOperationException("Data already deleted.");
            }

            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
            {
                using (ManagerDataEntities dataSource = new ManagerDataEntities(Connection))
                {
                    dataSource.Attach((Package)Data);

                    foreach (Template template in (from item in dataSource.Templates where item.Package.PackageID == ((Package)Data).PackageID select item))
                    {
                        template.PackageID = null;
                    }
                    dataSource.SaveChanges();
                    dataSource.DeleteObject(Data);
                    dataSource.SaveChanges();
                }
                scope.Complete();
            }

        }

        /// <summary>
        /// Saves the requested IPackageData
        /// </summary>
        /// <param name="Data">IPackageData to save to the repository</param>
        /// <exception cref="ArgumentNullException">Thrown if data is null</exception>
        /// <exception cref="InvalidCastException">Thrown if the data is of the wrong type for this datasource</exception>
        public void Save(IPackageData Data)
        {
            if (Data == null)
            {
                throw new ArgumentNullException("Data", "Must have a valid Data to delete");
            }

            if (!(Data is Package))
            {
                throw new InvalidCastException("Data is not of type Package. Cannot save this object through this repository");
            }

            if (!Data.IsDirty)
            {
                return;
            }

            using (ManagerDataEntities dataSource = new ManagerDataEntities(Connection))
            {
                if (Data.IsNew)
                {
                    dataSource.AddToPackages((Package)Data);
                    dataSource.SaveChanges();
                    dataSource.Detach(Data);
                }
                else
                {
                    object originalItem;

                    // Create the detached object's entity key.
                    EntityKey itemKey = dataSource.CreateEntityKey("Packages", Data);

                    // Get the original item based on the entity key from the context
                    // or from the database.
                    if (dataSource.TryGetObjectByKey(itemKey, out originalItem))
                    {
                        // Call the ApplyPropertyChanges method to apply changes
                        // from the updated item to the original version.
                        dataSource.ApplyPropertyChanges(itemKey.EntitySetName, Data);
                        dataSource.SaveChanges();
                    }
                }
            }
        }

        /// <summary>
        /// Creates a new instance of IPackageData
        /// </summary>
        /// <returns>New Instance of IPackageData</returns>
        public IPackageData Create()
        {
            return new Package();
        }

        #endregion
    }
}